import { _decorator, Component, instantiate, loader, log, Node, Prefab, resources, SpriteFrame } from 'cc';
import DataManager from '../Global/DataManager';
import { Joystick } from '../UI/Joystick';
import { ResourceManager } from '../Global/ResourceManager';
import { ActorMgr } from '../Entity/Actor/ActorMgr';
import { EventEnum, prefabPathEnum, TexturePathEnum } from '../Enum';
import { ApiMsgEnum, EntityTypeEnum, IClientInput, IMsgClientSync, IMsgServerSync, InputTypeEnum } from '../Common';
import { BulletMgr } from '../Entity/Bullet/BulletMgr';
import ObjectPoolManager from '../Global/ObjectPoolManager';
import { NetWorkManager } from '../Global/NetWorkManager';
import EventManager from '../Global/EventManager';
import { deepClone } from '../Utils';
const { ccclass, property } = _decorator;

@ccclass('BattleMgr')
export class BattleMgr extends Component {
    private ui:Node = null;
    private state:Node = null;
    private pendingMsg:IMsgClientSync[] = [];

    private shouldUpdate:boolean = false;
    protected onLoad(): void {}
    async start() {
        this.clearGame();
        await Promise.all([this.connectServer(),this.loadRes()]);
        this.initGame();
    }
    initGame(){
        DataManager.Instance.jm = this.ui.getComponentInChildren(Joystick);
        this.initMap();
        this.shouldUpdate = true;
        EventManager.Instance.on(EventEnum.ClientSync,this.handelClientSync,this)
        NetWorkManager.Instance.listenMsg(ApiMsgEnum.MsgServerSync,this.handleServerSync,this)
    }

    clearGame(){
        this.ui = this.node.getChildByName('UI');
        DataManager.Instance.stage = this.state = this.node.getChildByName('Stage');
        this.state.destroyAllChildren();
        EventManager.Instance.off(EventEnum.ClientSync,this.handelClientSync,this)
        NetWorkManager.Instance.unlistenMsg(ApiMsgEnum.MsgServerSync,this.handleServerSync,this)
    }

    async connectServer(){
        if(!await NetWorkManager.Instance.connect().catch(()=>false)){
            await new Promise((rs)=>{setTimeout(rs,1000)})
            log('连接中...');
            await this.connectServer();
        }
    }

    async loadRes(){
        log('加载资源');
        const list = []
        for (const type in prefabPathEnum) {
            const p = ResourceManager.Instance.loadRes(prefabPathEnum[type],Prefab).then((prefab)=>{
                DataManager.Instance.prefabMap.set(type,prefab);
                log(`加载${type}成功`);
            })
            list.push(p);
        }

        for (const type in TexturePathEnum) {
            const p = ResourceManager.Instance.loadDir(TexturePathEnum[type],SpriteFrame).then((spriteFrame)=>{
                DataManager.Instance.textureMap.set(type,spriteFrame);
                log(`加载${type}成功`);
            })
            list.push(p);
        }

        await Promise.all(list);
    }

    update(deltaTime: number) {
        if(!this.shouldUpdate) return;
        this.render();
        this.tick(deltaTime);
    }

    tick(dt:number){
        this.tickActor(dt);
    }

    tickActor(dt:number){
        for (const data of DataManager.Instance.State.actors) {
            const {id} = data;
            let am = DataManager.Instance.actorMap.get(id); 
            am.tick(dt);
        } 
    }

    initMap() {
        const prefab = DataManager.Instance.prefabMap.get(EntityTypeEnum.Map);
        const map = instantiate(prefab);
        map.setParent(this.state); 
    }

    render() {
        this.renderActors();
        this.renderBullet();
    }

    async renderActors() {
        for (const data of DataManager.Instance.State.actors) {
           const {id,type} = data;
           let am = DataManager.Instance.actorMap.get(id);
           if(!am) {
                const prefab = DataManager.Instance.prefabMap.get(type);
                const actor = instantiate(prefab);
                actor.setParent(this.state);
                am = actor.addComponent(ActorMgr);
                DataManager.Instance.actorMap.set(id,am);
                am.init(data);
           }else {
                am.render(data);
           }
        }
    }
    async renderBullet() {
        for (const data of DataManager.Instance.State.bullets) {
           const {id,type} = data;
           let bm = DataManager.Instance.bulletMap.get(id);
           if(!bm) {
                const bullet = ObjectPoolManager.Instance.get(type);
                
                bm = bullet.getComponent(BulletMgr) || bullet.addComponent(BulletMgr);
                DataManager.Instance.bulletMap.set(id,bm);
                bm.init(data);
           }else {
                bm.render(data);
           }
        }
    }

    handelClientSync(input:IClientInput){
        const msg = {
            input,
            frameId : DataManager.Instance.frameId++,
        }
        NetWorkManager.Instance.sendMsg(ApiMsgEnum.MsgClientSync,msg);

        if(input.type === InputTypeEnum.ActorMove){
            DataManager.Instance.applyInput(input);
            this.pendingMsg.push(msg);
        }
       
    }

    handleServerSync({inputs,lastFrameId}:IMsgServerSync){
        DataManager.Instance.State = DataManager.Instance.lastState;
        for (const input of inputs) {
            DataManager.Instance.applyInput(input); 
        }

        DataManager.Instance.lastState = deepClone(DataManager.Instance.State);
        this.pendingMsg = this.pendingMsg.filter((msg) => msg.frameId > lastFrameId);
        for (const msg of this.pendingMsg) {
            DataManager.Instance.applyInput(msg.input); 
        }
    }

}

